#include <SDL2/SDL.h>
#include <atomic>
#include <chrono>
#include <cmath> // for sin()
#include <cstring>
#include <fstream>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

const int RECORD_TIME = 5;
const int AUDIO_BUFFER_SIZE = 4096;

std::vector<Uint8> audioBuffer;
std::chrono::steady_clock::time_point recordStartTime;
std::atomic<bool> isRecording(false);
std::mutex audioMutex;

// 保存音频数据到文件
void saveAudioToFile(const char *filename, const std::vector<Uint8> &data) {
  std::ofstream file(filename, std::ios::binary);
  if (!file) {
    std::cerr << "无法打开文件进行写入: " << filename << std::endl;
    return;
  }

  file.write(reinterpret_cast<const char *>(data.data()), data.size());
  std::cout << "已将音频数据保存到文件: " << filename << " (" << data.size()
            << " 字节)" << std::endl;
}

// 生成测试音频
void generateTestAudio(std::vector<Uint8> &buffer) {
  const int duration = 5; // 5秒
  const int sampleRate = 44100;
  const int channels = 2;
  const int bytesPerSample = 2; // 16位

  const size_t numSamples = duration * sampleRate;
  buffer.resize(numSamples * channels * bytesPerSample);

  Sint16 *samples = reinterpret_cast<Sint16 *>(buffer.data());
  const double freq = 440.0; // A4 音符

  for (int i = 0; i < numSamples; i++) {
    double value = 0.5 * sin(2.0 * M_PI * freq * i / sampleRate);
    Sint16 sample = static_cast<Sint16>(value * 32767);

    for (int c = 0; c < channels; c++) {
      samples[i * channels + c] = sample;
    }
  }
}

// 播放音频数据
void playAudio(const std::vector<Uint8> &data, int freq, int channels,
               SDL_AudioFormat format) {
  // 创建播放数据结构
  struct PlaybackData {
    const Uint8 *data;
    size_t size;
    size_t pos;
  };

  PlaybackData *pbData = new PlaybackData{data.data(), data.size(), 0};

  SDL_AudioSpec spec;
  spec.freq = freq;
  spec.format = format;
  spec.channels = channels;
  spec.samples = 4096;
  spec.userdata = pbData;

  // 设置SDL音频回调函数，每当音频设备需要更多数据时会调用此函数
  spec.callback = [](void *userdata, Uint8 *stream, int len) {
    // 将userdata转换为PlaybackData结构，获取音频数据和当前播放进度
    PlaybackData *pbData = static_cast<PlaybackData *>(userdata);
    // 计算本次最多可以复制多少字节，防止越界
    size_t toCopy =
        std::min(static_cast<size_t>(len), pbData->size - pbData->pos);
    if (toCopy > 0) {
      // 从数据缓冲区复制音频数据到输出流
      std::copy(pbData->data + pbData->pos, pbData->data + pbData->pos + toCopy,
                stream);
      pbData->pos += toCopy; // 更新播放进度
    }

    // 如果数据不足，剩余部分用静音填充
    if (toCopy < static_cast<size_t>(len)) {
      memset(stream + toCopy, 0, len - toCopy);
    }
  };

  // 打开音频设备进行播放
  if (SDL_OpenAudio(&spec, nullptr)) {
    std::cerr << "无法打开音频设备用于播放: " << SDL_GetError() << std::endl;
    delete pbData;
    return;
  }

  SDL_PauseAudio(0);
  std::cout << "播放音频..." << std::endl;

  // 等待播放完成
  const int durationMs =
      (data.size() / (freq * channels * (SDL_AUDIO_BITSIZE(format) / 8))) *
      1000;
  SDL_Delay(durationMs + 500);

  SDL_PauseAudio(1);
  SDL_CloseAudio();
  delete pbData;
  std::cout << "播放完成" << std::endl;
}

void audioCaptureCallback(void *userdata, Uint8 *stream, int len) {
  if (isRecording) {
    std::lock_guard<std::mutex> lock(audioMutex);
    std::copy(stream, stream + len, std::back_inserter(audioBuffer));

    auto currentTime = std::chrono::steady_clock::now();
    auto elapsedTime = std::chrono::duration_cast<std::chrono::seconds>(
                           currentTime - recordStartTime)
                           .count();

    if (elapsedTime >= RECORD_TIME) {
      isRecording = false;
    }
  }
}

int main(int argc, char *argv[]) {
  // 强制使用ALSA驱动
  SDL_setenv("SDL_AUDIODRIVER", "alsa", 1);

  if (SDL_Init(SDL_INIT_AUDIO) < 0) {
    std::cerr << "无法初始化 SDL: " << SDL_GetError() << std::endl;
    return -1;
  }

  // 诊断信息：音频系统状态
  std::cout << "=== 音频系统诊断 ===" << std::endl;
  // SDL_version *linked;
  // SDL_GetVersion(linked);
  // std::cout << "SDL版本: "
  //           << static_cast<int>(linked->major) << "."
  //           << static_cast<int>(linked->minor) << "."
  //           << static_cast<int>(linked->patch) << std::endl;
  std::cout << "音频驱动: " << SDL_GetCurrentAudioDriver() << std::endl;

  // 枚举播放设备
  int playbackDevices = SDL_GetNumAudioDevices(0);
  std::cout << "\n可用的播放设备 (" << playbackDevices << "):" << std::endl;
  for (int i = 0; i < playbackDevices; i++) {
    std::cout << "  " << i << ": " << SDL_GetAudioDeviceName(i, 0) << std::endl;
  }

  // 枚举录音设备
  int recordingDevices = SDL_GetNumAudioDevices(1);
  std::cout << "\n可用的录音设备 (" << recordingDevices << "):" << std::endl;
  for (int i = 0; i < recordingDevices; i++) {
    std::cout << "  " << i << ": " << SDL_GetAudioDeviceName(i, 1) << std::endl;
  }

  // 测试1: 播放生成的测试音
  std::cout << "\n=== 测试1: 播放生成的测试音 ===" << std::endl;
  std::vector<Uint8> testAudio;
  generateTestAudio(testAudio);
  playAudio(testAudio, 44100, 2, AUDIO_S16SYS);
  saveAudioToFile("generated_audio.pcm", testAudio);

  // 测试2: 录制音频
  std::cout << "\n=== 测试2: 录制音频 ===" << std::endl;

  // 设置录音规格
  SDL_AudioSpec captureSpec;
  captureSpec.freq = 44100;
  captureSpec.format = AUDIO_S16SYS;
  captureSpec.channels = 2;
  captureSpec.samples = AUDIO_BUFFER_SIZE;
  captureSpec.callback = audioCaptureCallback;
  captureSpec.userdata = nullptr;

  // 尝试打开录音设备
  SDL_AudioDeviceID captureDevId = 0;
  const char *recordDevice = nullptr;

  // 尝试不同的录音设备
  for (int i = 0; i < recordingDevices; i++) {
    recordDevice = SDL_GetAudioDeviceName(i, 1);
    std::cout << "尝试打开录音设备: " << recordDevice << std::endl;

    captureDevId = SDL_OpenAudioDevice(recordDevice, // 设备名称
                                       1,            // 1=录制
                                       &captureSpec, nullptr, 0);

    if (captureDevId != 0) {
      std::cout << "成功打开录音设备" << std::endl;
      break;
    } else {
      std::cerr << "  失败: " << SDL_GetError() << std::endl;
    }
  }

  if (captureDevId == 0) {
    std::cerr << "无法打开任何录音设备" << std::endl;
    SDL_Quit();
    return -1;
  }

  // 重置状态
  {
    std::lock_guard<std::mutex> lock(audioMutex);
    audioBuffer.clear();
  }
  isRecording = true;
  recordStartTime = std::chrono::steady_clock::now();

  // 开始录音
  SDL_PauseAudioDevice(captureDevId, 0);
  std::cout << "开始录音..." << std::endl;

  // 等待录音完成
  const auto start = std::chrono::steady_clock::now();
  while (isRecording) {
    auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(
                       std::chrono::steady_clock::now() - start)
                       .count();

    if (elapsed > RECORD_TIME + 2) {
      std::cerr << "录音超时，强制结束" << std::endl;
      isRecording = false;
      break;
    }

    // 显示进度
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    if (static_cast<int>(elapsed * 2) % 2 == 0) { // 每秒显示一次
      std::lock_guard<std::mutex> lock(audioMutex);
      std::cout << "录音中... " << elapsed << "/" << RECORD_TIME << " 秒"
                << ", 已录制 " << audioBuffer.size() << " 字节" << std::endl;
    }
  }

  // 停止录音
  SDL_PauseAudioDevice(captureDevId, 1);
  SDL_CloseAudioDevice(captureDevId);

  size_t recordedSize = 0;
  {
    std::lock_guard<std::mutex> lock(audioMutex);
    recordedSize = audioBuffer.size();
  }

  std::cout << "录音完成，录制 " << recordedSize << " 字节" << std::endl;

  if (recordedSize > 0) {
    // 保存录音数据
    saveAudioToFile("recorded_audio.pcm", audioBuffer);

    // 分析录音数据
    bool allZero = true;
    {
      std::lock_guard<std::mutex> lock(audioMutex);
      for (size_t i = 0; i < std::min(recordedSize, static_cast<size_t>(100));
           i++) {
        if (audioBuffer[i] != 0) {
          allZero = false;
          break;
        }
      }
    }

    if (allZero) {
      std::cerr << "警告: 录音数据前100字节全为0，可能未捕获到声音"
                << std::endl;
    }

    // 测试3: 播放录音
    std::cout << "\n=== 测试3: 播放录音 ===" << std::endl;
    playAudio(audioBuffer, 44100, 2, AUDIO_S16SYS);
  } else {
    std::cerr << "无录音数据可播放" << std::endl;
  }

  // 测试4: 使用系统工具播放录音文件
  if (recordedSize > 0) {
    std::cout << "\n=== 测试4: 使用系统工具播放录音文件 ===" << std::endl;
    std::cout << "运行以下命令播放录音文件:" << std::endl;
    std::cout
        << "ffplay -f s16le -ar 44100 -ac 2 -f pcm_s16le -i recorded_audio.pcm"
        << std::endl;
    std::cout << "或者使用其他播放器播放" << std::endl;
  }

  SDL_Quit();
  return 0;
}